
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: MapField.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar;

import java.util.Map;

import org.dellroad.sidekar.query.QueryMap;

/**
 * A {@link Field} with map semantics, where the keys and values of the map are {@link SimpleField}s.
 *
 * @param <K> key type
 * @param <V> value type
 * @see org.dellroad.sidekar.annotation.SidekarMap
 */
public final class MapField<K, V> extends ComplexField<Map<K, V>> {

    private final SimpleField<K> keyField;
    private final SimpleField<V> valueField;

    MapField(String name, Entity<?> entity, SimpleField<K> keyField, SimpleField<V> valueField) {
        super(name, entity, Map.class);
        this.keyField = keyField;
        this.valueField = valueField;
    }

    /**
     * @throws DetachedItemException {@inheritDoc}
     * @throws IllegalArgumentException {@inheritDoc}
     * @throws IllegalStateException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     * @throws org.dellroad.sidekar.util.SidekarException {@inheritDoc}
     */
    @Override
    public QueryMap<K, V> get(Item<?> item) {
        this.validate();
        item.sidekarValidate();
        return this.getDatastore().mapFieldAccess(this.getEntity(), item.getSidekarId(), this);
    }

    /**
     * Get the field type that corresponds to this map's key type.
     *
     * <p>
     * The returned field will have no name associated with it and cannot be used
     * to set any values.
     */
    public SimpleField<K> getKeyField() {
        return this.keyField;
    }

    /**
     * Get the field type that corresponds to this map's value type.
     *
     * <p>
     * The returned field will have no name associated with it and cannot be used
     * to set any values.
     */
    public SimpleField<V> getValueField() {
        return this.valueField;
    }

    @Override
    public void visit(FieldSwitch fieldSwitch) {
        fieldSwitch.caseMapField(this);
    }

    @Override
    public boolean canReference(Entity<?> entity) {
        return this.keyField.canReference(entity) || this.valueField.canReference(entity);
    }
}

